package com.h3c.ptability.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.eos.common.util.IdWorker;
import com.google.common.base.Charsets;
import com.h3c.ptability.bo.ElectronicSignBO;
import com.h3c.ptability.cache.UserCache;
import com.h3c.ptability.config.properties.ElectronicSignProperties;
import com.h3c.ptability.dto.ExcelFileDTO;
import com.h3c.ptability.dto.electronicSign.PtEsignRecordDTO;
import com.h3c.ptability.dto.electronicSign.callback.FinishDocUrlBean;
import com.h3c.ptability.entity.*;
import com.h3c.ptability.enums.ElectronicSignRecordInfoStatus;
import com.h3c.ptability.enums.ElectronicSignRecordStatus;
import com.h3c.ptability.enums.NeedSignEnum;
import com.h3c.ptability.mapper.PtEsignRecordMapper;
import com.h3c.ptability.mapstruct.PtEsignRecordInfoMapstruct;
import com.h3c.ptability.mapstruct.PtEsignRecordMapstruct;
import com.h3c.ptability.service.*;
import com.h3c.ptability.utils.MqUtils;
import com.h3c.ptability.utils.PageUtils;
import com.h3c.ptability.vo.PtEsignRecordDownloadVO;
import com.h3c.ptability.vo.PtEsignRecordVO;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.net.URLEncoder;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 员工签署明细电子文件记录实现类
 *
 * @author chenyifan
 * @since 2022-05-19 10:20:00
 */
@Slf4j
@Service
@AllArgsConstructor
public class PtEsignRecordServiceImpl extends ServiceImpl<PtEsignRecordMapper, PtEsignRecord> implements IPtEsignRecordService {

    private final IPtEsignDocFeatureService docFeatureService;

    private final IPtEsignRecordInfoService recordInfoService;

    private final IPtEsignDocTemplateService docTemplateService;

    private final ElectronicSignProperties electronicSignProperties;

    private final IPtEnumService ptEnumService;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String saveEsignRecord(Boolean success,
                                  JSONObject userInfo,
                                  Integer sceneType,
                                  String sceneId,
                                  String appId,
                                  String failReason,
                                  JSONArray jsonArray,
                                  String signFlowId,
                                  String signUrl, String signProcessInfo,String creatorInfo) {
        PtEsignRecord ptEsignRecord = new PtEsignRecord();
        String recordId = IdWorker.nextId() + "";
        ptEsignRecord
                .setAppId(appId)
                .setCreatorInfo(creatorInfo)
                .setFailReason(failReason)
                .setRecordId(recordId)
                .setSceneType(sceneType)
                .setSignBeginTime(new Date())
                .setWorkerId(userInfo.getStr(ElectronicSignBO.WORKER_ID))
                .setWorkerName(userInfo.getStr(ElectronicSignBO.WORKER_NAME));
        if (success) {
            List<PtEsignSignProcessInfo> signProcessInfos = JSONUtil.toList(signProcessInfo, PtEsignSignProcessInfo.class);
            signProcessInfos.forEach(s -> s.setSignStatus(PtEsignSignProcessInfo.NO_SIGN));
            ptEsignRecord.setSignUrl(signUrl);
            ptEsignRecord.setSignProcessInfo(signProcessInfos);
            ptEsignRecord.setSignStatus(ElectronicSignRecordStatus.SIGNING);
            ptEsignRecord.setSignFlowId(signFlowId);
        } else {
            ptEsignRecord.setSignStatus(ElectronicSignRecordStatus.SIGN_FAIL);
            ptEsignRecord.setFailReason(failReason);
        }
        save(ptEsignRecord);
        if (Objects.nonNull(sceneId) && StringUtils.isNotBlank(sceneId)) {
            ArrayList<PtEsignRecordInfo> recordInfos = new ArrayList<>();
            List<PtEsignDocTemplate> templateList = docTemplateService.getTemplateListBySceneId(sceneId);
            for (PtEsignDocTemplate template : templateList) {
                PtEsignRecordInfo recordInfo = new PtEsignRecordInfo();
                if (success) {
                    recordInfo.setStatus(ElectronicSignRecordInfoStatus.SIGNING);
                    recordInfo.setUserDocFileKey(getDocFileKeyByDocName(template.getDocName(), jsonArray));
                } else {
                    recordInfo.setStatus(ElectronicSignRecordInfoStatus.SIGN_FAIL);
                }
                recordInfo
                        .setId(IdWorker.nextId() + "")
                        .setRecordId(recordId)
                        .setTemplateName(template.getDocName())
                        .setSignTime(new Date())
                        .setTemplateId(template.getTemplateId());
                recordInfos.add(recordInfo);
            }
            recordInfoService.saveBatch(recordInfos);
        }
        log.info("生成电子签章记录完成recordId为:{}", recordId);
        return recordId;
    }

    /**
     * 根据文件名称获取文件对应的docFilekey
     *
     * @param docName
     * @param jsonArray
     * @return
     */
    private String getDocFileKeyByDocName(String docName, JSONArray jsonArray) {
        String docFileKey = null;
        if (Objects.nonNull(jsonArray)) {
            for (JSONObject entries : jsonArray.jsonIter()) {
                if (StringUtils.equals(entries.getStr("docName"), docName.concat(".pdf"))) {
                    docFileKey = entries.getStr("docFilekey");
                    break;
                }
            }
        }
        return docFileKey;
    }

    @Override
    public List<String> judgeWorkerIdsNormal(List<String> workerIdList, Integer sceneType) {
        return list(new QueryWrapper<PtEsignRecord>().lambda()
                .in(PtEsignRecord::getWorkerId, workerIdList)
                .eq(PtEsignRecord::getSceneType, sceneType)
                .in(PtEsignRecord::getSignStatus, Arrays.asList(ElectronicSignRecordStatus.SIGN_FAIL,
                        ElectronicSignRecordStatus.REFUSE_SIGN))
        ).stream().map(PtEsignRecord::getWorkerId).collect(Collectors.toList());
    }

    @Override
    public Map<String, List<String>> judgeRecordIsOver(String flowId) {
        PtEsignRecord record = getRecordByFlowId(flowId);
        List<PtEsignRecordInfo> recordInfos = recordInfoService.getRecordInfosByRecordId(record.getRecordId());
        List<String> templateIds = recordInfos.stream()
                .map(PtEsignRecordInfo::getTemplateId)
                .collect(Collectors.toList());
        HashMap<String, List<String>> result = new HashMap<>(templateIds.size());

        List<PtEsignDocTemplate> templates = docTemplateService.getTemplateListByIds(templateIds);
        List<String> needOrgSignIds = templates.stream().filter(s -> s.getNeedOrgSign().equals(NeedSignEnum.NEED_SIGN)).map(PtEsignDocTemplate::getTemplateId).collect(Collectors.toList());
        result.put("UN_OVER", recordInfos.stream().filter(s -> needOrgSignIds.contains(s.getTemplateId())).map(PtEsignRecordInfo::getUserDocFileKey).collect(Collectors.toList()));
        List<String> overTemplateIds = templateIds.stream().filter(s -> !needOrgSignIds.contains(s)).collect(Collectors.toList());
        result.put("OVER", recordInfos.stream().filter(s -> overTemplateIds.contains(s.getTemplateId())).map(PtEsignRecordInfo::getUserDocFileKey).collect(Collectors.toList()));
        return result;
    }

    @Override
    public PtEsignRecord getRecordByFlowId(String flowId) {
        return getOne(new QueryWrapper<PtEsignRecord>()
                .lambda()
                .eq(PtEsignRecord::getSignFlowId, flowId)
        );
    }

    @Override
    public void updateRecordByFlowId(String flowId, PtEsignRecord esignRecord) {
        update(esignRecord, new QueryWrapper<PtEsignRecord>().lambda()
                .eq(PtEsignRecord::getSignFlowId, flowId)
        );
    }

    @Override
    public String getWorkerIdByFlowId(String flowId) {
        PtEsignRecord record = getOne(new QueryWrapper<PtEsignRecord>().lambda()
                .eq(PtEsignRecord::getSignFlowId, flowId)
        );
        if (Objects.isNull(record)) {
            record = getOne(new QueryWrapper<PtEsignRecord>().lambda()
                    .eq(PtEsignRecord::getOrgFlowId, flowId)
            );
        }
        return record.getWorkerId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteRecordAndInfoById(String recordId) {
        removeById(recordId);
        recordInfoService.remove(new QueryWrapper<PtEsignRecordInfo>().lambda()
                .eq(PtEsignRecordInfo::getRecordId, recordId)
        );
    }

    @Override
    public void saveCallBackParams(String flowId, List<FinishDocUrlBean> docUrlBeanList, Boolean isWaste) {
        PtEsignRecord record;
        if (isWaste) {
            record = getRecordByCancelFlowId(flowId);
        } else {
            record = getRecordByFlowId(flowId);
        }
        List<PtEsignRecordInfo> recordInfos = recordInfoService.list(new QueryWrapper<PtEsignRecordInfo>().lambda()
                .eq(PtEsignRecordInfo::getRecordId, record.getRecordId())
        );
        for (FinishDocUrlBean docUrlBean : docUrlBeanList) {
            recordInfos.stream().filter(s -> s.getUserDocFileKey().equals(docUrlBean.getDocFileKey()))
                    .findFirst()
                    .ifPresent(s -> s.setDocUrlBean(docUrlBean));
        }
        recordInfoService.updateBatchById(recordInfos);
    }

    @Override
    public void wasteRecordAndInfoById(PtEsignRecord record, List<String> cancelFileInfos) {
        List<PtEsignRecordInfo> esignRecordInfos = recordInfoService.list(new QueryWrapper<PtEsignRecordInfo>().lambda()
                .eq(PtEsignRecordInfo::getRecordId, record.getRecordId())
        );
        if (record.getSignFlowId().equals(PtEsignRecord.ALL_ORG_SIGN)) {
            record.setSignStatus(ElectronicSignRecordStatus.WASTE_SIGN_FINISH);
        } else if(esignRecordInfos.size()==1){
            PtEsignDocFeature docFeature = docFeatureService.list(new QueryWrapper<PtEsignDocFeature>().lambda()
                    .eq(PtEsignDocFeature::getTemplateId, esignRecordInfos.get(0).getTemplateId())
                    .orderByAsc(PtEsignDocFeature::getSignOrder)
            ).get(0);
            //如果只有一个文件并且由公司先进行签署
            if(StringUtils.isNotBlank(docFeature.getOrgInfoName())){
                record.setSignStatus(ElectronicSignRecordStatus.WAIT_ORG_SIGN_WASTE);
                //发送邮件通知进行公司作废
                List<PtEsignSignProcessInfo> orgNoSign = record.getCancelProcessInfo().stream()
                        .filter(s -> "1".equals(s.getAccountType()) && s.getSignStatus().equals(PtEsignSignProcessInfo.NO_SIGN))
                        .collect(Collectors.toList());
                for (PtEsignSignProcessInfo signProcessInfo : orgNoSign) {
                    MqUtils.pushEmailJobToNem(record.getWorkerId(), signProcessInfo.getSignUrl(),
                            electronicSignProperties.getNoticeNemTalentOrgSignWasteEmailTemplateCode());
                }
            }else{
                record.setSignStatus(ElectronicSignRecordStatus.WAIT_USER_SIGN_WASTE);
            }
        }else{
            record.setSignStatus(ElectronicSignRecordStatus.WAIT_USER_SIGN_WASTE);
        }
        updateById(record);
        int i = 0;
        for (PtEsignRecordInfo esignRecordInfo : esignRecordInfos) {
            esignRecordInfo.setCancelDocFileKey(cancelFileInfos.get(i));
            i++;
        }
        recordInfoService.updateBatchById(esignRecordInfos);
    }

    @Override
    public PtEsignRecord getRecordByCancelFlowId(String cancelFlowId) {
        return getOne(new QueryWrapper<PtEsignRecord>()
                .lambda()
                .eq(PtEsignRecord::getCancelFlowId, cancelFlowId)
        );
    }

    @Override
    public IPage<PtEsignRecordVO> selectPage(PtEsignRecordDTO recordDTO) {
        LambdaQueryWrapper<PtEsignRecord> queryWrapper = new QueryWrapper<PtEsignRecord>().lambda();
        queryWrapper.orderByDesc(PtEsignRecord::getSignBeginTime);
        if (org.springframework.util.StringUtils.hasText(recordDTO.getWorkerId())) {
            queryWrapper.eq(PtEsignRecord::getWorkerId, recordDTO.getWorkerId());
        }
        if (Objects.nonNull(recordDTO.getSceneType())) {
            queryWrapper.eq(PtEsignRecord::getSceneType, recordDTO.getSceneType());
        }
        if (Objects.nonNull(recordDTO.getSignBeginTime())) {
            queryWrapper.ge(PtEsignRecord::getSignBeginTime, recordDTO.getSignBeginTime());
            queryWrapper.lt(PtEsignRecord::getSignBeginTime, DateUtil.offsetDay(recordDTO.getSignBeginTime(), 1));
        }
        if (Objects.nonNull(recordDTO.getSignStatus())) {
            queryWrapper.eq(PtEsignRecord::getSignStatus, recordDTO.getSignStatus());
        }
        if (org.springframework.util.StringUtils.hasText(recordDTO.getAppId())) {
            queryWrapper.eq(PtEsignRecord::getAppId, recordDTO.getAppId());
        }
        if (!CollectionUtils.isEmpty(recordDTO.getWorkerIdList())) {
            queryWrapper.in(PtEsignRecord::getWorkerId, recordDTO.getWorkerIdList());
        }
        if (!CollectionUtils.isEmpty(recordDTO.getSceneTypeList())) {
            queryWrapper.in(PtEsignRecord::getSceneType, recordDTO.getSceneTypeList());
        }
        if (!CollectionUtils.isEmpty(recordDTO.getSignStatusList())) {
            queryWrapper.in(PtEsignRecord::getSignStatus, recordDTO.getSignStatusList());
        }
        Page<PtEsignRecord> recordPage = page(new Page<>(recordDTO.getQuery().getPageNum(), recordDTO.getQuery().getPageSize()), queryWrapper);
        List<PtEsignRecordVO> recordVOS = PtEsignRecordMapstruct.INSTANCE.entityListToVoList(recordPage.getRecords());
        for (PtEsignRecordVO record : recordVOS) {
            List<PtEsignRecordInfo> recordInfos = recordInfoService.getRecordInfosByRecordId(record.getRecordId());
            record.setInfoVOList(PtEsignRecordInfoMapstruct.INSTANCE.entityListToVoList(recordInfos));
        }
        return PageUtils.toPage(recordPage, recordVOS);
    }

    @Override
    @SneakyThrows
    public void downloadRecord(PtEsignRecordDTO recordDTO, HttpServletResponse response){
        List<PtEsignRecord> esignRecords = baseMapper.selectWorkerIndoRecord(recordDTO);
        List<PtEsignRecordDownloadVO> recordVOS = PtEsignRecordMapstruct.INSTANCE.entityListToDownloadVoList(esignRecords);
        ExcelFileDTO excelFileDTO = new ExcelFileDTO();
        excelFileDTO.setAppId("ptability-manage");
        excelFileDTO.setDataList(recordVOS);
        excelFileDTO.setFileName("签署记录".concat(DateUtil.format(new Date(),"yyyy年MM月dd日")));
        excelFileDTO.setTemplateCode("PT_ESIGN_RECORD_EXPORT");
        File downLoadFile = ptEnumService.getExcelFile(excelFileDTO);
        FileInputStream inputStream = new FileInputStream(downLoadFile);
        byte[] buffer = new byte[(int) downLoadFile.length()];
        inputStream.read(buffer);
        inputStream.close();
        FileUtil.del(downLoadFile);
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding(Charsets.UTF_8.name());
        response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(downLoadFile.getName(), Charsets.UTF_8.name()));
        response.setHeader("issuccess", "1");
        response.setHeader("Access-Control-Expose-Headers", "Content-disposition,issuccess");
        IoUtil.write(response.getOutputStream(), true, buffer);
    }
}

